package com.example.tourism.service.impl;

import com.example.tourism.mapper.OrderMapper;
import com.example.tourism.pojo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static com.example.tourism.pojo.Result.error;
import static com.example.tourism.pojo.Result.success;

@Service
public class SeckillServiceImpl {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final ExecutorService executor = Executors.newFixedThreadPool(10);
    @Autowired
    private OrderMapper orderMapper;
    // sismember key value  是redis判断value是否存在key集合中 1存在 0不存在
    private final static String luaScript = """
                local productKey = KEYS[1]
                local userKey = KEYS[2]
                local stock = tonumber(redis.call('get', productKey))

                if not stock then
                    return -1
                end

                if stock > 0 then
                    local userExists = redis.call('sismember', userKey, ARGV[1])
                    if userExists == 0 then
                        redis.call('decr', productKey)
                        redis.call('sadd', userKey, ARGV[1])
                        return 1
                    else
                        return 2
                    end
                else
                    return 0
                end
                """;

    public Result attemptSeckill(String productId, String userId) {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(Long.class);
        String productKey = "seckill:stock:" + productId;
        String userKey = "seckill:users:" + productId;
        System.out.println(productKey+"\n"+userKey);
        Long result = redisTemplate.execute(redisScript, Arrays.asList(productKey, userKey), userId);

        if (result != null && result == -1) {
            // 使用分布式锁确保初始化库存操作只执行一次
            Boolean isLock = redisTemplate.opsForValue().setIfAbsent("seckill:lock:" + productId, "LOCK", 5, TimeUnit.SECONDS);
            if (Boolean.TRUE.equals(isLock)) {
                try {
                    Long stock = getStockFromDatabase(productId);
                    if (stock != null && stock > 0) {
                        redisTemplate.opsForValue().set(productKey, String.valueOf(stock));
                        result = redisTemplate.execute(redisScript, Collections.singletonList(productKey), userKey, userId);
                    } else {
                        return error(404, "优惠券库存不存在或已售罄！");
                    }
                } finally {
                    // 释放分布式锁
                    redisTemplate.delete("seckill:lock:" + productId);
                }
            } else {
                return error(403, "库存初始化中，请稍后再试！");
            }
        }

        // 由lua脚本 执行之后的结果进行判断
        if (result != null && result == 1) {
            // 异步保存抢购成功订单
            // saveOrderAsync(productId, userId);
            orderMapper.insertUserCoupon(userId, productId);
        }

        return switch (result != null ? result.intValue() : -1) {
            case 1 -> success("抢购成功！");
            case 2 -> error(401, "您已经抢购过了2222！");
            case 0 -> error(402, "库存不足！");
            default -> error(400, "抢购失败，请稍后重试！");
        };
    }

    /**
     * 异步保存订单到数据库
     */
    private void saveOrderAsync(String productId, String userId) {
        try {
            executor.submit(() -> orderMapper.insertUserCoupon(userId, productId));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 模拟从数据库查询商品库存
     */
    private Long getStockFromDatabase(String productId) {
        return orderMapper.queryStockByProductId(productId);
    }

    /**
     * 定期同步Redis库存到数据库
     * 每1天执行一次
     */

//    @Scheduled( cron = "0 0/1 * * * ?")  // 每分钟执行一次
    @Scheduled( cron = "0 0 0 * * ?")  // 每天凌晨0点执行
    public void syncStockToDatabase() {
        // 获取所有需要同步的商品库存
        String pattern = "seckill:stock:*";
        System.out.println("同步");
        for (String key : redisTemplate.keys(pattern)) {
            String productId = key.replace("seckill:stock:", "");
            Object stock = redisTemplate.opsForValue().get(key);
            // 更新数据库库存
            if (stock != null) {
                System.out.println("同步商品" + productId + "库存到数据库"+"库存为"+stock.toString()+"");
               orderMapper.updateCoupon(productId, Integer.parseInt(stock.toString()));
            }
        }
    }

//    @Scheduled(cron = "0/10 * * * * ?") // 每10秒执行一次
//    public void testTask() {
//        System.out.println("测试任务执行：" + LocalDateTime.now());
//    }


}
